from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer
import logging
import os
import sys
import socket
import platform
import time
from threading import Lock

# === 系统检测 ===
IS_WINDOWS = platform.system().lower() == "windows"
IS_LINUX = platform.system().lower() == "linux"
IS_MAC = platform.system().lower() == "darwin"

# === 配置区域 - 可修改这些变量来自定义服务器 ===
FTP_CONFIG = {
    # 端口设置：优先尝试21端口，失败后使用备用端口
    "PREFERRED_PORT": 21,  # 首选端口
    "FALLBACK_PORTS": [2121, 2122, 2123, 2124, 2125],  # 备用端口
    # 用户认证配置
    "USERNAME": "hello",  # FTP用户名
    "PASSWORD": "hello",  # FTP密码
    # 服务器配置
    "MAX_CONNECTIONS": 50,  # 最大连接数
    "MAX_CONNECTIONS_PER_IP": 5,  # 每IP最大连接数
    "TIMEOUT": 300,  # 连接超时时间（秒）
    # 共享目录设置
    "SHARE_DIR_NAME": "shareDir",  # 共享目录名称
}

# === 配置日志 ===
LOG_DIR = os.path.join(os.path.dirname(__file__), "logs")
os.makedirs(LOG_DIR, exist_ok=True)

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s",
    handlers=[
        logging.FileHandler(os.path.join(LOG_DIR, "ftp_server.log"), encoding="utf-8"),
        logging.StreamHandler(),
    ],
)
logger = logging.getLogger()

# === 共享目录（当前脚本所在目录下的共享目录） ===
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
SHARED_DIR = os.path.join(CURRENT_DIR, FTP_CONFIG["SHARE_DIR_NAME"])
os.makedirs(SHARED_DIR, exist_ok=True)


class FileTransferTracker:
    """文件传输跟踪器"""

    def __init__(self):
        self.transfers = {}  # 存储正在进行的传输
        self.lock = Lock()

    def start_transfer(
        self, transfer_id, username, remote_ip, filename, direction, filesize=0
    ):
        """开始跟踪文件传输"""
        with self.lock:
            self.transfers[transfer_id] = {
                "username": username,
                "remote_ip": remote_ip,
                "filename": filename,
                "direction": direction,  # 'upload' 或 'download'
                "filesize": filesize,
                "start_time": time.time(),
                "bytes_transferred": 0,
                "last_update": time.time(),
            }

    def update_transfer(self, transfer_id, bytes_transferred):
        """更新传输进度"""
        with self.lock:
            if transfer_id in self.transfers:
                transfer = self.transfers[transfer_id]
                transfer["bytes_transferred"] = bytes_transferred
                transfer["last_update"] = time.time()

                # 计算进度和速度
                progress = (
                    (bytes_transferred / transfer["filesize"]) * 100
                    if transfer["filesize"] > 0
                    else 0
                )
                elapsed_time = time.time() - transfer["start_time"]
                speed = bytes_transferred / elapsed_time if elapsed_time > 0 else 0

                # 每传输1MB或进度超过10%时记录一次
                if bytes_transferred % (1024 * 1024) == 0 or progress % 10 == 0:
                    logger.info(
                        f"传输进度 [{transfer['direction'].upper()}] - "
                        f"用户: {transfer['username']} "
                        f"文件: {os.path.basename(transfer['filename'])} "
                        f"进度: {progress:.1f}% "
                        f"({self._format_size(bytes_transferred)} / {self._format_size(transfer['filesize'])}) "
                        f"速度: {self._format_speed(speed)}"
                    )

    def complete_transfer(self, transfer_id):
        """完成文件传输"""
        with self.lock:
            if transfer_id in self.transfers:
                transfer = self.transfers.pop(transfer_id)
                total_time = time.time() - transfer["start_time"]
                avg_speed = (
                    transfer["bytes_transferred"] / total_time if total_time > 0 else 0
                )

                logger.info(
                    f"传输完成 [{transfer['direction'].upper()}] - "
                    f"用户: {transfer['username']} "
                    f"文件: {os.path.basename(transfer['filename'])} "
                    f"大小: {self._format_size(transfer['bytes_transferred'])} "
                    f"耗时: {total_time:.1f}s "
                    f"平均速度: {self._format_speed(avg_speed)}"
                )

    def _format_size(self, size_bytes):
        """格式化文件大小"""
        if size_bytes == 0:
            return "0 B"
        size_names = ["B", "KB", "MB", "GB"]
        i = 0
        while size_bytes >= 1024 and i < len(size_names) - 1:
            size_bytes /= 1024.0
            i += 1
        return f"{size_bytes:.2f} {size_names[i]}"

    def _format_speed(self, speed_bytes_per_sec):
        """格式化传输速度"""
        if speed_bytes_per_sec == 0:
            return "0 B/s"
        speed_names = ["B/s", "KB/s", "MB/s", "GB/s"]
        i = 0
        while speed_bytes_per_sec >= 1024 and i < len(speed_names) - 1:
            speed_bytes_per_sec /= 1024.0
            i += 1
        return f"{speed_bytes_per_sec:.2f} {speed_names[i]}"


# 全局文件传输跟踪器
transfer_tracker = FileTransferTracker()


class CustomFTPHandler(FTPHandler):
    """自定义FTP处理器，增强日志功能"""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.current_transfer_id = None
        self.current_username = None

    def log(self, msg, logfun=None):
        """过滤FTP日志，只记录重要信息"""
        if any(keyword in msg for keyword in ["220", "421", "500", "530"]):
            logger.debug(f"FTP协议: {msg}")

    def logerror(self, msg):
        """记录错误信息"""
        logger.error(f"FTP错误: {msg}")

    def on_connect(self):
        """客户端连接时的处理"""
        logger.info(f"📡 客户端连接: {self.remote_ip}:{self.remote_port}")

    def on_disconnect(self):
        """客户端断开时的处理"""
        logger.info(f"🔌 客户端断开: {self.remote_ip}:{self.remote_port}")
        # 清理可能未完成的传输
        if hasattr(self, "current_transfer_id") and self.current_transfer_id:
            transfer_tracker.complete_transfer(self.current_transfer_id)

    def on_login(self, username):
        """用户登录时的处理"""
        self.current_username = username
        logger.info(f"🔐 用户登录: {username} 来自 {self.remote_ip}")

    # === 文件删除操作 ===
    def ftp_DELE(self, path):
        """删除文件"""
        file_path = self.fs.realpath(path)

        # 记录删除操作
        if os.path.exists(file_path):
            file_size = os.path.getsize(file_path)
            file_info = f"大小: {transfer_tracker._format_size(file_size)}"
        else:
            file_info = "文件不存在"

        logger.warning(
            f"🗑️  删除文件 - "
            f"用户: {self.current_username} "
            f"从: {self.remote_ip} "
            f"文件: {os.path.basename(file_path)} "
            f"{file_info} "
            f"路径: {file_path}"
        )

        try:
            result = super().ftp_DELE(path)
            logger.info(
                f"✅ 文件删除成功 - "
                f"用户: {self.current_username} "
                f"文件: {os.path.basename(file_path)}"
            )
            return result
        except Exception as e:
            logger.error(
                f"❌ 文件删除失败 - "
                f"用户: {self.current_username} "
                f"文件: {os.path.basename(file_path)} "
                f"错误: {e}"
            )
            raise

    def ftp_RMD(self, path):
        """删除目录"""
        dir_path = self.fs.realpath(path)

        # 记录目录删除操作
        if os.path.exists(dir_path):
            # 统计目录中的文件和子目录数量
            file_count = 0
            dir_count = 0
            total_size = 0

            for root, dirs, files in os.walk(dir_path):
                dir_count += len(dirs)
                file_count += len(files)
                for file in files:
                    file_path = os.path.join(root, file)
                    if os.path.exists(file_path):
                        total_size += os.path.getsize(file_path)

            dir_info = f"包含: {file_count}个文件, {dir_count}个子目录, 总大小: {transfer_tracker._format_size(total_size)}"
        else:
            dir_info = "目录不存在"

        logger.warning(
            f"🗑️  删除目录 - "
            f"用户: {self.current_username} "
            f"从: {self.remote_ip} "
            f"目录: {os.path.basename(dir_path)} "
            f"{dir_info} "
            f"路径: {dir_path}"
        )

        try:
            result = super().ftp_RMD(path)
            logger.info(
                f"✅ 目录删除成功 - "
                f"用户: {self.current_username} "
                f"目录: {os.path.basename(dir_path)}"
            )
            return result
        except Exception as e:
            logger.error(
                f"❌ 目录删除失败 - "
                f"用户: {self.current_username} "
                f"目录: {os.path.basename(dir_path)} "
                f"错误: {e}"
            )
            raise

    # === 文件重命名操作 ===
    def ftp_RNFR(self, from_path):
        """重命名 - 源文件"""
        self.rename_from = from_path
        return "350 Ready for destination name"

    def ftp_RNTO(self, to_path):
        """重命名 - 目标文件"""
        if hasattr(self, "rename_from"):
            from_path = self.fs.realpath(self.rename_from)
            to_path_full = self.fs.realpath(to_path)

            # 记录重命名操作
            from_info = ""
            if os.path.exists(from_path):
                if os.path.isfile(from_path):
                    file_size = os.path.getsize(from_path)
                    from_info = f"大小: {transfer_tracker._format_size(file_size)}"
                else:
                    from_info = "目录"

            logger.info(
                f"📝 重命名 - "
                f"用户: {self.current_username} "
                f"从: {self.remote_ip} "
                f"原文件: {os.path.basename(from_path)} → 新文件: {os.path.basename(to_path_full)} "
                f"{from_info} "
                f"路径: {from_path} → {to_path_full}"
            )

            try:
                result = super().ftp_RNTO(to_path)
                logger.info(
                    f"✅ 重命名成功 - "
                    f"用户: {self.current_username} "
                    f"{os.path.basename(from_path)} → {os.path.basename(to_path_full)}"
                )
                return result
            except Exception as e:
                logger.error(
                    f"❌ 重命名失败 - "
                    f"用户: {self.current_username} "
                    f"{os.path.basename(from_path)} → {os.path.basename(to_path_full)} "
                    f"错误: {e}"
                )
                raise
        else:
            return "503 Bad sequence of commands"

    # === 文件创建操作 ===
    def ftp_MKD(self, path):
        """创建目录"""
        dir_path = self.fs.realpath(path)

        logger.info(
            f"📁 创建目录 - "
            f"用户: {self.current_username} "
            f"从: {self.remote_ip} "
            f"目录: {os.path.basename(dir_path)} "
            f"路径: {dir_path}"
        )

        try:
            result = super().ftp_MKD(path)
            logger.info(
                f"✅ 目录创建成功 - "
                f"用户: {self.current_username} "
                f"目录: {os.path.basename(dir_path)}"
            )
            return result
        except Exception as e:
            logger.error(
                f"❌ 目录创建失败 - "
                f"用户: {self.current_username} "
                f"目录: {os.path.basename(dir_path)} "
                f"错误: {e}"
            )
            raise

    # === 文件传输操作 ===
    def ftp_RETR(self, file):
        """处理文件下载（客户端从服务器获取文件）"""
        file_path = self.fs.realpath(file)
        file_size = os.path.getsize(file_path) if os.path.exists(file_path) else 0

        # 生成传输ID
        self.current_transfer_id = f"{self.remote_ip}_{int(time.time() * 1000)}"

        # 开始跟踪下载
        transfer_tracker.start_transfer(
            self.current_transfer_id,
            self.current_username or "unknown",
            self.remote_ip,
            file_path,
            "download",
            file_size,
        )

        logger.info(
            f"⬇️  开始下载 - "
            f"用户: {self.current_username} "
            f"从: {self.remote_ip} "
            f"文件: {os.path.basename(file_path)} "
            f"大小: {transfer_tracker._format_size(file_size)} "
            f"目标: {file_path}"
        )

        try:
            result = super().ftp_RETR(file)
            return result
        except Exception as e:
            logger.error(f"❌ 下载失败: {file_path} - 错误: {e}")
            raise

    def ftp_STOR(self, file, mode="w"):
        """处理文件上传（客户端向服务器发送文件）"""
        file_path = self.fs.realpath(file)

        # 生成传输ID
        self.current_transfer_id = f"{self.remote_ip}_{int(time.time() * 1000)}"

        # 开始跟踪上传（文件大小未知）
        transfer_tracker.start_transfer(
            self.current_transfer_id,
            self.current_username or "unknown",
            self.remote_ip,
            file_path,
            "upload",
        )

        logger.info(
            f"⬆️  开始上传 - "
            f"用户: {self.current_username} "
            f"从: {self.remote_ip} "
            f"文件: {os.path.basename(file_path)} "
            f"目标: {file_path}"
        )

        try:
            result = super().ftp_STOR(file, mode)
            return result
        except Exception as e:
            logger.error(f"❌ 上传失败: {file_path} - 错误: {e}")
            raise

    def on_file_sent(self, file):
        """文件发送完成时的处理（下载完成）"""
        if hasattr(self, "current_transfer_id") and self.current_transfer_id:
            # 更新最终文件大小
            file_path = self.fs.realpath(file)
            if os.path.exists(file_path):
                file_size = os.path.getsize(file_path)
                transfer_tracker.transfers[self.current_transfer_id][
                    "filesize"
                ] = file_size
                transfer_tracker.update_transfer(self.current_transfer_id, file_size)

            transfer_tracker.complete_transfer(self.current_transfer_id)
            self.current_transfer_id = None

        logger.info(
            f"✅ 下载完成 - "
            f"用户: {self.current_username} "
            f"从: {self.remote_ip} "
            f"文件: {os.path.basename(file)}"
        )

    def on_file_received(self, file):
        """文件接收完成时的处理（上传完成）"""
        if hasattr(self, "current_transfer_id") and self.current_transfer_id:
            # 更新最终文件大小
            file_path = self.fs.realpath(file)
            if os.path.exists(file_path):
                file_size = os.path.getsize(file_path)
                transfer_tracker.transfers[self.current_transfer_id][
                    "filesize"
                ] = file_size
                transfer_tracker.update_transfer(self.current_transfer_id, file_size)

            transfer_tracker.complete_transfer(self.current_transfer_id)
            self.current_transfer_id = None

        logger.info(
            f"✅ 上传完成 - "
            f"用户: {self.current_username} "
            f"从: {self.remote_ip} "
            f"文件: {os.path.basename(file)}"
        )

    def on_incomplete_file_sent(self, file):
        """文件发送未完成"""
        logger.warning(
            f"⚠️  下载中断 - "
            f"用户: {self.current_username} "
            f"从: {self.remote_ip} "
            f"文件: {os.path.basename(file)}"
        )
        if hasattr(self, "current_transfer_id") and self.current_transfer_id:
            transfer_tracker.complete_transfer(self.current_transfer_id)
            self.current_transfer_id = None

    def on_incomplete_file_received(self, file):
        """文件接收未完成"""
        logger.warning(
            f"⚠️  上传中断 - "
            f"用户: {self.current_username} "
            f"从: {self.remote_ip} "
            f"文件: {os.path.basename(file)}"
        )
        if hasattr(self, "current_transfer_id") and self.current_transfer_id:
            transfer_tracker.complete_transfer(self.current_transfer_id)
            self.current_transfer_id = None

    # 重写数据传输方法以跟踪进度
    def _make_dataport_connection(self, handler, type):
        """创建数据端口连接，用于跟踪传输进度"""
        conn = super()._make_dataport_connection(handler, type)

        # 包装连接对象以跟踪数据传输
        class ProgressTrackingConnection:
            def __init__(self, conn, ftp_handler):
                self.conn = conn
                self.ftp_handler = ftp_handler
                self.bytes_transferred = 0

            def send(self, data):
                sent = self.conn.send(data)
                self.bytes_transferred += sent
                if (
                    hasattr(self.ftp_handler, "current_transfer_id")
                    and self.ftp_handler.current_transfer_id
                ):
                    transfer_tracker.update_transfer(
                        self.ftp_handler.current_transfer_id, self.bytes_transferred
                    )
                return sent

            def recv(self, bufsize):
                data = self.conn.recv(bufsize)
                received = len(data)
                self.bytes_transferred += received
                if (
                    hasattr(self.ftp_handler, "current_transfer_id")
                    and self.ftp_handler.current_transfer_id
                ):
                    transfer_tracker.update_transfer(
                        self.ftp_handler.current_transfer_id, self.bytes_transferred
                    )
                return data

            def close(self):
                return self.conn.close()

            def __getattr__(self, name):
                return getattr(self.conn, name)

        return ProgressTrackingConnection(conn, self)


# 其他函数保持不变（get_local_ip, check_port_availability, is_admin_user, find_available_port, setup_ftp_server, print_connection_info, print_config_summary, main）


def get_local_ip():
    """获取本机IP地址"""
    try:
        with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
            s.connect(("8.8.8.8", 80))
            return s.getsockname()[0]
    except:
        return "127.0.0.1"


def check_port_availability(port):
    """检查端口是否可用"""
    try:
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            s.settimeout(1)
            s.bind(("0.0.0.0", port))
            return True
    except:
        return False


def is_admin_user():
    """检查是否具有管理员权限（用于绑定21端口）"""
    if IS_WINDOWS:
        try:
            import ctypes

            return ctypes.windll.shell32.IsUserAnAdmin() != 0
        except:
            return False
    else:
        try:
            return os.geteuid() == 0
        except AttributeError:
            return False


def find_available_port():
    """查找可用的端口，优先使用21端口"""
    preferred_port = FTP_CONFIG["PREFERRED_PORT"]
    fallback_ports = FTP_CONFIG["FALLBACK_PORTS"]

    if preferred_port == 21:
        if check_port_availability(preferred_port):
            if preferred_port < 1024 and not is_admin_user():
                logger.warning(f"端口 {preferred_port} 需要管理员权限，将使用备用端口")
            else:
                logger.info(f"端口 {preferred_port} 可用")
                return preferred_port
        else:
            logger.warning(f"端口 {preferred_port} 被占用，尝试备用端口")

    for port in fallback_ports:
        if check_port_availability(port):
            logger.info(f"使用备用端口: {port}")
            return port

    for port in range(21210, 21300):
        if check_port_availability(port):
            logger.info(f"自动选择可用端口: {port}")
            return port

    import random

    while True:
        port = random.randint(10000, 65535)
        if check_port_availability(port):
            logger.warning(f"使用随机端口: {port}")
            return port


def setup_ftp_server(port):
    """设置FTP服务器"""
    authorizer = DummyAuthorizer()
    username = FTP_CONFIG["USERNAME"]
    password = FTP_CONFIG["PASSWORD"]

    authorizer.add_user(username, password, SHARED_DIR, perm="elradfmw")

    handler = CustomFTPHandler
    handler.authorizer = authorizer

    handler.passive_ports = range(60000, 60100)
    handler.timeout = FTP_CONFIG["TIMEOUT"]
    max_cons = FTP_CONFIG["MAX_CONNECTIONS"]
    max_cons_per_ip = FTP_CONFIG["MAX_CONNECTIONS_PER_IP"]
    handler.banner = f"PyFTPD Server Ready on port {port}"

    server = FTPServer(("0.0.0.0", port), handler)
    server.max_cons = max_cons
    server.max_cons_per_ip = max_cons_per_ip

    import pyftpdlib.log

    pyftpdlib.log.LEVEL = pyftpdlib.log.logging.ERROR

    return server, max_cons, max_cons_per_ip


def print_connection_info(port):
    """打印连接信息"""
    local_ip = get_local_ip()
    username = FTP_CONFIG["USERNAME"]
    password = FTP_CONFIG["PASSWORD"]

    print("\n" + "=" * 60)
    print("🎯 FTP服务器启动成功!")
    print("=" * 60)
    print(f"📁 共享目录: {SHARED_DIR}")
    print(f"🌐 服务器地址: {local_ip}:{port}")
    print(f"👤 用户名: {username}")
    print(f"🔑 密码: {password}")
    print(f"🔗 连接方式:")
    print(f"   - FTP客户端: ftp://{local_ip}:{port}")
    print(f"   - 浏览器: ftp://{username}:{password}@{local_ip}:{port}/")
    if IS_WINDOWS:
        print(
            f"   - Windows资源管理器: 在地址栏输入: ftp://{username}:{password}@{local_ip}:{port}"
        )
    print(
        f"📊 最大连接数: {FTP_CONFIG['MAX_CONNECTIONS']}, 每IP最大连接: {FTP_CONFIG['MAX_CONNECTIONS_PER_IP']}"
    )

    if port == 21:
        print("💡 提示: 使用标准FTP端口21，客户端连接时无需指定端口")
    else:
        print(f"💡 提示: 使用端口{port}，客户端连接时需要指定端口")

    if port < 1024 and not is_admin_user():
        print("⚠️  注意: 当前端口需要管理员权限，建议以管理员身份运行")

    print("⏹️  按 Ctrl+C 停止服务器")
    print("=" * 60)
    print()


def print_config_summary():
    """打印配置摘要"""
    print("🔧 服务器配置摘要:")
    print(
        f"   端口策略: 优先{FTP_CONFIG['PREFERRED_PORT']} → 备用{FTP_CONFIG['FALLBACK_PORTS']}"
    )
    print(f"   用户名: {FTP_CONFIG['USERNAME']}")
    print(f"   密码: {'*' * len(FTP_CONFIG['PASSWORD'])}")
    print(f"   共享目录: {SHARED_DIR}")
    print()


def main():
    """主函数"""
    try:
        logger.info(f"操作系统: {platform.system()} {platform.release()}")
        logger.info(f"Python版本: {platform.python_version()}")
        print_config_summary()

        if not os.path.exists(SHARED_DIR):
            os.makedirs(SHARED_DIR)
            logger.info(f"创建共享目录: {SHARED_DIR}")

        logger.info("正在检查端口可用性...")
        ftp_port = find_available_port()

        server, max_cons, max_cons_per_ip = setup_ftp_server(ftp_port)
        print_connection_info(ftp_port)

        logger.info(f"FTP服务器启动在 0.0.0.0:{ftp_port}")
        logger.info(f"共享目录: {SHARED_DIR}")
        logger.info(f"用户名: {FTP_CONFIG['USERNAME']}")
        logger.info(f"最大连接数: {max_cons}, 每IP最大连接: {max_cons_per_ip}")

        logger.info("服务器运行中...")
        server.serve_forever()

    except PermissionError as e:
        logger.error(f"权限错误: {e}")
        if ftp_port < 1024:
            logger.info("提示: 1024以下端口需要管理员权限")
            if IS_WINDOWS:
                logger.info("请在管理员命令提示符中运行此脚本")
            else:
                logger.info("请使用sudo运行: sudo python ftpServer.py")

    except OSError as e:
        if "Address already in use" in str(e):
            logger.error(f"端口 {ftp_port} 已被占用")
            logger.info("正在尝试其他端口...")
            try:
                new_port = find_available_port()
                server, max_cons, max_cons_per_ip = setup_ftp_server(new_port)
                print_connection_info(new_port)
                logger.info(f"重新启动在端口: {new_port}")
                server.serve_forever()
            except Exception as retry_e:
                logger.error(f"重新启动失败: {retry_e}")
        else:
            logger.error(f"系统错误: {e}")

    except KeyboardInterrupt:
        logger.info("收到停止信号，正在关闭服务器...")
    except Exception as e:
        logger.error(f"未知错误: {e}")
    finally:
        try:
            if "server" in locals():
                server.close_all()
                logger.info("服务器已安全关闭")
        except Exception as e:
            logger.error(f"关闭服务器时出错: {e}")

        logger.info("FTP服务已停止")
        print("\n感谢使用FTP服务器!")


if __name__ == "__main__":
    os.chdir(os.path.dirname(os.path.abspath(__file__)))
    main()
